<?php

/**
 * Author: LCY<409028735@qq.com>
 * Time: 2021/11/19 18:45
 * Note:
 */

namespace app\admin\library;

use app\admin\model\Admin;
use think\Db;
use think\facade\Config;
use think\facade\Cookie;
use think\facade\Session;
use util\Tree;

class Auth extends \util\Auth
{
    /**
     * @var string
     * 错误提示信息
     */
    protected $error = '';

    /**
     * @var bool
     * 是否已经登录
     */
    protected $logined = false;

    public function __construct($noNeedAuth = [])
    {
        parent::__construct($noNeedAuth);
    }

    //快捷获取用户的属性值
    public function __get($key)
    {
        return Session::get('user.' . $key, $this->_config['AUTH_USER'].'_');
    }


    /**
     * 登录验证
     * @param string $username
     * @param string $password
     * @param int $keepTime
     * @return bool
     * Author: LCY<409028735@qq.com>
     * Time: 2021/11/20 15:59
     * Note:
     */
    public function login($username, $password, $keepTime = 0)
    {
        $admin = Admin::getByUsername($username);
        if (!$admin) {
            $this->setError('账号有误');
            return false;
        }

        if ($admin->status == 0) {
            $this->setError('账号已被禁用');
            return false;
        }

        if ($admin->password != encrypt_password($password)) {
            $this->setError('密码有误');
            return false;
        }

        $admin->login_time = time();
        $admin->login_ip = request()->ip();
        $admin->token = md5(uniqid($password, true));
        $admin->save();

        Session::set('user', $admin->toArray(), $this->_config['AUTH_USER'].'_');

        $this->keepLogin($keepTime);
        return true;
    }

    /**
     * 实时更新用户存放在session 中的数据
     * @param null $uid
     * Author: LCY<409028735@qq.com>
     * Time: 2021/12/25 23:09
     * Note:
     */
    public function updateUserInfoSession($uid = null)
    {
        $info = $this->getUserInfo($uid);
        Session::set('user', $info, $this->_config['AUTH_USER'].'_');
    }

    /**
     * 退出登录
     * @return bool
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     * Author: LCY<409028735@qq.com>
     * Time: 2021/11/20 16:15
     * Note:
     */
    public function logout()
    {
        $user = Admin::find($this->id);
        if ($user) {
            $user->token = '';
            $user->save();
        }

        $this->logined = false;
        Session::clear($this->_config['AUTH_USER'].'_');
        Cookie::clear($this->_config['AUTH_USER'].'_');
        return true;
    }

    //保持登录
    public function keepLogin($keepTime = 0)
    {
        if ($keepTime) {
            $expireTime = time() + $keepTime;
            $data = [$this->id, $expireTime, $keepTime, $this->token];
            $key = encrypt_password(implode($data));
            $data[] = $key;
            Cookie::set('keep_login', implode('|', $data), ['prefix' => $this->_config['AUTH_USER'].'_', 'expire' => $keepTime]);
            return true;
        }

        return false;
    }

    //自动登录
    public function autoLogin()
    {
        $keepLogin = Cookie::get('keep_login', $this->_config['AUTH_USER'].'_');
        //halt($keepLogin);
        if (!$keepLogin) {
            return false;
        }
        try {
            list($uid, $expireTime, $keepTime, $token, $key) = explode('|', $keepLogin);
            if (encrypt_password($uid . $expireTime . $keepTime . $token) != $key || $expireTime < time()) {
                return false;
            }
            $admin = Admin::find($uid);
            if (!$admin || !$admin->token || $admin->login_ip != request()->ip()) {
                return false;
            }
            Session::set('user', $admin->toArray(), $this->_config['AUTH_USER'].'_');
            $this->keepLogin($keepTime);
        } catch (\Exception $e) {
            return false;
        }


        return true;
    }

    //检测是否登录
    public function isLogin()
    {
        if ($this->logined) {
            return true;
        }
        $user = Session::get('user', $this->_config['AUTH_USER'].'_');
        if (!$user) {
            return false;
        }
        //验证单点登录
        if (Config::get('auth.login_unique', false)) {
            $admin = Admin::find($user['id']);
            if (!$admin || $admin->token != $user['token']) {
                $this->logined = false;
                Session::clear($this->_config['AUTH_USER'].'_');
                Cookie::clear($this->_config['AUTH_USER'].'_');
                return false;
            }
        }

        //验证登录的ip是否变动
        if (Config::get('auth.login_ip_check', false) && (empty($user['login_id']) || $user['login_id'] != request()->ip())) {
            $this->logined = false;
            return false;
        }


        $this->logined = true;
        return true;
    }

    /**
     * 检查权限
     * @param $name string|array 需要验证的规则列表,支持逗号分隔的权限规则或索引数组
     * @param $uid  int          认证用户的id
     * @param $relation string   如果为 'or' 表示满足任一条规则即通过验证;如果为 'and'则表示需满足所有规则才能通过验证
     * @param $mode string       执行check的模式【主要是为了验证当url中存在参数时的情况，~~可能是为了区分其他方式的验证模式】
     * @return boolean           通过验证返回true;失败返回false
     */
    public function check($name, $uid = null, $relation = 'or', $mode = 'url')
    {
        $uid = $uid ? $uid : $this->id;
        return parent::check($name, $uid, $relation, $mode);
    }

    /**
     * 获取用于前台展示的菜单
     *
     * @return void
     */
    public function getFrontendMemus($uid = 0){
        $uid = $uid ?: $this->id;
        $menus = $this->getMenus($uid);
        $menus = Tree::dataToTree($menus);
        return $menus;
    }
    /**
     * 查询当前登录用户拥有的菜单
     * @return array|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * Author: LCY<409028735@qq.com>
     * Time: 2021/11/29 18:18
     * Note:
     */
    public function getMenus($uid = 0)
    {
        $uid = $uid ?: $this->id;
        //查询当前登录用户拥有的菜单
        static $_menus = []; //保存用户验证通过的权限列表

        if (isset($_menus[$uid])) {
            return $_menus[$uid];
        }
        if (2 == $this->_config['AUTH_TYPE'] && $list = session('_MENU_LIST_' . $uid, $this->_config['AUTH_USER'].'_')) {
            return $list;
        }

        $ids = $this->getRuleIds();
        if (empty($ids)) {
            $_menus[$uid] = [];
            return [];
        }

        $map[] = ['status', '=', 1];
        $map[] = ['is_menu', '=', 1];
        if (!in_array('*', $ids)) {
            $map[] = ['id', 'in', $ids];
        }
        //读取用户组所有权限规则
        $rules = Db::name($this->_config['AUTH_RULE'])->where($map)->order(['sort' => 'asc', 'id' => 'asc'])->select();
        //循环规则，判断结果。
        $authList = []; //
        foreach ($rules as $rule) {
            //判断是否有额外的规则条件
            if (!empty($rule['condition'])) {
                //根据condition进行验证
                $user = $this->getUserInfo($uid); //获取用户信息,一维数组
                //替换[rule表中的附加规则]如"{score}>10"为[用户信息表中的字段的值]如"$user['score']>10"
                //正则说明 "/\{(\w*?)\}/" => 匹配 "{score}" , 替换内容 中\\1表示匹配第一个(),
                $command = preg_replace('/\{(\w*?)\}/', '$user[\'\\1\']', $rule['condition']);
                //生成规则如：$user['score']>10 ；把字符串作为php代码实现
                @(eval('$condition=(' . $command . ');'));
                //符合条件就记录
                if ($condition) {
                    $authList[] = $rule;
                }
            } else {
                //只要存在就记录
                $authList[] = $rule;
            }
        }
        $_menus[$uid] = $authList;
        if (2 == $this->_config['AUTH_TYPE']) {
            //规则列表结果保存到session
            Session::set('_MENU_LIST_' . $uid, $authList, $this->_config['AUTH_USER'].'_');
        }

        return $authList;
    }

    /**
     * 从登录用户已有的菜单中获取第一个默认打开的菜单id
     *
     * @return void
     */
    public function getDefaultMenuIdFromMenus()
    {
        $menus = $this->getmenus();
        if (empty($menus)) {
            return 0;
        }

        $treeDatas = Tree::dataToTree($menus);
        $menuId = $this->getFirstChildMenuId($treeDatas[0]);
        return $menuId ?: 0;
    }


    /**
     * 查询tree结构列表的最末端层级的第一个子元素
     *
     * @param array $menu  单个菜单
     * @return int  
     */
    public function getFirstChildMenuId($menu)
    {
        if (empty($menu['child'])) {
            return $menu['id'];
        }
        return $this->getFirstChildMenuId($menu['child'][0]);
    }

    public function getPathIdsByRuleName($ruleName = '', $menus = [])
    {
        if (empty($ruleName) || empty($menus)) {
            return [];
        }
        $menu_id = Db::name($this->_config['AUTH_RULE'])->where('name', $ruleName)->value('id');
    }

    /**
     * 通过规则名称查询菜单信息
     * @param string $ruleName
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     * Author: LCY<409028735@qq.com>
     * Time: 2021/11/30 8:30
     * Note:
     */
    public function getDataByRuleName($ruleName = '')
    {
        $menu = Db::name($this->_config['AUTH_RULE'])->where('name', $ruleName)->find();
        return $menu ?: [];
    }

    /**
     * 获取用户所有用户组拥有的所有权限
     * @param int $uid
     * @return array
     * Author: LCY<409028735@qq.com>
     * Time: 2021/12/1 20:31
     * Note:
     */
    public function getRuleIds($uid = null)
    {
        $uid = $uid ? $uid : $this->id;
        return parent::getRuleIds($uid);
    }

    /**
     * 判断某用户是否是超级管理员
     * @param int $uid
     * @return bool
     * Author: LCY<409028735@qq.com>
     * Time: 2021/12/1 20:34
     * Note:
     */
    public function isSuperAdmin($uid = null)
    {
        $ruleIds = $this->getRuleIds($uid);
        return in_array('*', $ruleIds) ? true : false;
    }

    /**
     * 获取用户的拥有的权限列表
     * @param int $uid
     * @return array|mixed
     * Author: LCY<409028735@qq.com>
     * Time: 2021/12/6 9:52
     * Note:
     */
    public function getRules($uid = null)
    {
        $uid = $uid ? $uid : $this->id;
        return  parent::getRules($uid);
    }

    /**
     * 获取用户拥有的权限JsTree版本
     * @param null $uid
     * @param string $checked_rules
     * @return array
     * Author: LCY<409028735@qq.com>
     * Time: 2021/12/6 10:06
     * Note:
     */
    public function getRuleTree($uid = null, $checked_rules = '')
    {
        $uid = $uid ? $uid : $this->id;
        $rules = $this->getRules($uid);
        is_string($checked_rules) && $checked_rules = explode(',', $checked_rules);
        return  Tree::dataToTreeForJstree($rules, $checked_rules);
    }

    /**
     * 查询用户信息
     * @param $uid
     * @return mixed
     * Author: LCY<409028735@qq.com>
     * Time: 2021/12/25 22:56
     * Note:
     */
    public function getUserInfo($uid = null)
    {
        $uid = $uid ? $uid : $this->id;
        return parent::getUserInfo($uid);
    }


    /**
     * 设置错误提示信息
     * @param string $error
     * Author: LCY<409028735@qq.com>
     * Time: 2021/11/20 11:20
     * Note:
     */
    public function setError($error = '')
    {
        $this->error =   $error;
    }

    /**
     * 获取错误提示信息
     * @return string
     * Author: LCY<409028735@qq.com>
     * Time: 2021/11/20 11:20
     * Note:
     */
    public function getError()
    {
        return $this->error;
    }
}
